package com.wenqiner.requesttool.util;

import com.wenqiner.requesttool.core.exception.CustomException;
import com.wenqiner.requesttool.domain.HttpConstant;
import com.wenqiner.requesttool.domain.HttpResponseData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;

/**
 * @author rhf
 * @date 2023-02-27
 **/
public class HttpUtil {

    public static final Logger log = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * post
     *
     * @param urlPath   路径
     * @param headerMap 请求头
     * @param jsonParam 参数
     * @author rhf
     */
    public static HttpResponseData<String> doPost(String urlPath, Map<String, String> headerMap, String jsonParam) {
        try {
            log.info("发送HTTP POST请求,url:[{}],header:[{}],params:[{}]", urlPath, headerMap, jsonParam);
            return request("POST", urlPath, headerMap, jsonParam);
        } catch (CustomException exception) {
            throw exception;
        } catch (Exception e) {
            log.error("发送HTTP POST请求异常, error msg:{}, \ne:{}", e.getMessage(), e);
            throw new CustomException("创建http请求失败");
        }

    }

    public HttpResponseData<String> doGet(String urlPath, Map<String, String> headerMap, String jsonParam, Boolean keepAlive) {
        try {
            log.info("发送HTTP GET请求,url:[{}],header:[{}],params:[{}]", urlPath, headerMap, jsonParam);
            return request("GET", urlPath, headerMap, jsonParam);
        } catch (CustomException exception) {
            throw exception;
        } catch (Exception e) {
            log.error("HTTP GET请求异常, error msg:[{}], \ne:{}", e.getMessage(), e);
            throw new CustomException("创建http请求失败");
        }
    }

    public static HttpResponseData<String> request(String method, String urlPath, Map<String, String> headerMap, String jsonParam) throws Exception {
        //建立连接
        URL url = new URL(urlPath);
        //如果需要添加证书此处返回HttpsURLConnection，使用setSSLSocketFactory()
        //以及涉及是否验证服务器setHostnameVerifier，默认false，false为验证，也可以根据参数的实现方法的第一个参数选择是否验证，即HostnameVerifier(){@Override Public boolean verify(String arg0,SSLSession arg1){return true;}}
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        //设置参数
        httpConn.setRequestProperty("Content-Length", String.valueOf(jsonParam.length()));
        httpConn.setConnectTimeout(HttpConstant.CONNECTION_TIMEOUT);
        httpConn.setReadTimeout(HttpConstant.READ_TIMEOUT);
        if (HttpConstant.KEEPALIVE) {
            // 维持长连接
            httpConn.setRequestProperty("Connection", "Keep-Alive");
        } else {
            httpConn.setRequestProperty("Connection", "close");
        }
        //需要输出
        httpConn.setDoOutput(true);
        //需要输入
        httpConn.setDoInput(true);
        //不允许缓存
        httpConn.setUseCaches(false);
        //设置POST方式连接
        httpConn.setRequestMethod(method);
        //设置请求属性
        httpConn.setRequestProperty("Content-Type", HttpConstant.CONTENT_TYPE);
        httpConn.setRequestProperty("Charset", HttpConstant.DEFAULT_CHARSET);
        if (null != headerMap) {
            for (String headName : headerMap.keySet()) {
                httpConn.addRequestProperty(headName, headerMap.get(headName));
            }
        }
        long beginTime = System.currentTimeMillis();
        //连接,也可以不用明文connect，使用下面的httpConn.getOutputStream()会自动connect
        httpConn.connect();

        //建立输入流，向指向的URL传入参数,此处如果是非get请求才使用,否则经过验证将会使httpConn.setRequestMethod("GET)设置不生效,反而成post请求
        DataOutputStream dos = new DataOutputStream(httpConn.getOutputStream());
        //参数中包含中文是使用此方法会乱码，应该使用dos.write()传入编码的字节
        dos.writeBytes(jsonParam);
        dos.flush();
        dos.close();
        //获得响应状态
        int resultCode = httpConn.getResponseCode();
        try (InputStream inputStream = httpConn.getInputStream()) {
            if (HttpURLConnection.HTTP_OK == resultCode) {
                StringBuilder sb = new StringBuilder();
                String readLine;
                BufferedReader responseReader = new BufferedReader(new InputStreamReader(inputStream, HttpConstant.DEFAULT_CHARSET));
                while ((readLine = responseReader.readLine()) != null) {
                    sb.append(readLine).append("\n");
                }
                Map<String, List<String>> headerFields = httpConn.getHeaderFields();
                responseReader.close();
                HttpResponseData<String> httpResponseData = new HttpResponseData<>();
                httpResponseData.setData(sb.toString());
                httpResponseData.setResponseHeader(headerFields);
                long endTime = System.currentTimeMillis();
                httpResponseData.setMsg("请求耗时:" + (endTime - beginTime) + "ms");
                httpResponseData.setConnectTime(endTime - beginTime);
                httpResponseData.setHttpCode(resultCode);
                return httpResponseData;
            } else {
                throw new CustomException("http请求失败,HttpCode:" + resultCode);
            }
        }
    }


    public static SSLSocketFactory setCertificates(String certPath, String password) {
        try {
            InputStream certStream = new FileInputStream(certPath);
            KeyStore ks = KeyStore.getInstance(HttpConstant.CERT_FORMAT);
            // 实例化密钥库 & 初始化密钥工厂
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            if (null != password && password.length() > 0) {
                ks.load(certStream, password.toCharArray());
                kmf.init(ks, password.toCharArray());
            } else {
                ks.load(certStream, null);
                kmf.init(ks, null);
            }

            // 创建 SSLContext
            SSLContext sslContext = SSLContext.getInstance(HttpConstant.TLS);
            sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
            return sslContext.getSocketFactory();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
